package com.chu.cloud.service.impl;

import com.chu.cloud.dto.DiscountCouponDto;
import com.chu.cloud.dto.MemberCouponDto;
import com.chu.cloud.entity.ShoppingCart;
import com.chu.cloud.enums.CartEventType;
import com.chu.cloud.enums.CouponEnum;
import com.chu.cloud.feign.client.IProductFeignClient;
import com.chu.cloud.repository.ShoppingCartRepository;
import com.chu.cloud.response.ResponseMessage;
import com.chu.cloud.service.ShoppingCartService;
import com.chu.cloud.util.BeanUtil;
import com.chu.cloud.util.ChuException;
import com.chu.cloud.util.JsonUtils;
import com.chu.cloud.vo.*;
import com.google.common.collect.Lists;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * @Description:
 * @author: TianShu.CHU
 * @CreateDate: 2018-05-06 16:20
 * @Version: 1.0
 */
@Service
@Slf4j
public class ShoppingCartServiceImpl implements ShoppingCartService {

    @Autowired
    private ShoppingCartRepository cartRepository;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private IProductFeignClient productFeignClient;

    @Override
    public void addCart(ShoppingCart cart) {
        ShoppingCart carts = cartRepository.findByMemberIdAndProductIdAndCartEvent(cart.getMemberId(), cart.getProductId(), CartEventType.ADD_ITEM);
        if (carts == null) {
            cartRepository.save(cart);
        } else {
            carts.setNum(cart.getNum() + carts.getNum());
            cartRepository.save(carts);
        }
    }

    @Override
    public Boolean removeCartItem(Integer productId, Integer memberId) {
        ShoppingCart shoppingCart = Optional.ofNullable(cartRepository.findByMemberIdAndProductIdAndCartEvent(memberId,productId, CartEventType.ADD_ITEM))
                .orElseThrow(() -> new ChuException("没有找到该用户的购物车项"));

        shoppingCart.setCartEvent(CartEventType.REMOVE_ITEM);
        cartRepository.save(shoppingCart);
        return true;

    }

    @HystrixCommand(fallbackMethod = "fallbackCart")
    @Override
    public ShoppingCartResult getShoppingCart(Integer memberId) {
        List<ShoppingCart> shoppingCarts = cartRepository.findByMemberIdAndCartEvent(memberId, CartEventType.ADD_ITEM);
        ShoppingCartResult result = aggregateUserCarts(shoppingCarts);
        return result;
    }

    /**
     * 检出购物车
     * 实际业务,用户选择检出的购物车项,传递cartItemIds集合即可,非重点
     * 这里,采用检出用户所有ADD_ITEM事件的购物车项,
     * <p>
     * 1.用户是否设置默认收货地址---->可以放到open-service中校验
     * 2.购物车中是否含有失效商品,有两种采用方案:
     * (1).抛出异常,不让检出
     * (2).检出成功,不参与订单逻辑
     * ------>当用户获取购物车时候,聚合刷选了失效和有效的购物车列表,
     * -->那么检出购物车的时候,前端可以不控制不检出失效的,后台仍然要重新聚合购物车
     * 3.校验购物车中的商品是否有足够库存
     * 4.上述检验成功之后:
     * (1).列出用户拥有的当前时间可使用的优惠券
     * <p>
     * </p>
     *
     * @return
     */
    @Override
    public CheckoutResult checkoutCart(Integer memberId) {
        CheckoutResult checkoutResult = new CheckoutResult();
        List<ShoppingCart> shoppingCarts = cartRepository.findByMemberIdAndCartEvent(memberId, CartEventType.ADD_ITEM);
        if (shoppingCarts.size() == 0) {
            return new CheckoutResult();
        }
        ShoppingCartResult currentCart = aggregateUserCarts(shoppingCarts);
        List<ShopCart> shopCart = currentCart.getShopCart();
        checkoutResult.setLoseProducts(currentCart.getLoseProducts());

        //当前购物车商品与库存的Map
        Map<Integer, Integer> productStorck = this.productStock(shopCart);

        if (checkAvailableInventory(currentCart, productStorck)) {
            //用户领取的店铺可用优惠券信息
            String memberCouponUrl = "http://chu-user/memberCoupon/search/findByMemberIdAndShopId?memberId=%s&shopId=%s";
            for (ShopCart cart : shopCart) {
                Integer shopId = cart.getId();
                //计算店铺购物车总价
                cart.computePrice();
                log.info("当前店铺购物车总价为:{}", cart.getShopCartPrice());
                String remoteUrl = String.format(memberCouponUrl, memberId, shopId);
                MemberCouponDto[] resouces = restTemplate.getForObject(remoteUrl, MemberCouponDto[].class);
                if (resouces.length > 0) {
                    List<MemberCouponDto> couponList = Arrays.asList(resouces);
                    //过滤已经使用的
                    couponList = couponList.stream().filter(c -> c.getStatus().equals(CouponEnum.GET)).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(couponList)) {
                        //当前时间可使用的
                        List<MemberCouponDto> enableUseCoupons = couponList.stream().filter(coupon ->
                                new Date().compareTo(coupon.getUseStartDate()) >= 0 &&
                                        new Date().compareTo(coupon.getExpirationDate()) <= 0
                        ).collect(Collectors.toList());
                        List<MemberCouponVo> couponVoList = mergeCoupons(enableUseCoupons);
                        //TODO 求出可使用的店铺优惠券
                        log.info("当前用户:{},可用的店铺:{}优惠券:{}", memberId, shopId, JsonUtils.obj2json(enableUseCoupons));
                        cart.setMemberCoupons(couponVoList);
                    }
                }
            }
            //返回数据
            checkoutResult.setShopCart(shopCart);
            BigDecimal totalPrice = checkoutResult.getShopCart().stream().
                    map(cart -> cart.getShopCartPrice().get(cart.getId())).
                    reduce(BigDecimal.ZERO, BigDecimal::add);
            checkoutResult.setTotalPrice(totalPrice);
            Boolean success = this.changeCartEvent(shopCart.stream().map(cart -> cart.getId()).
                    collect(Collectors.toList())
            );
        }
        //检查可用库存出异常,返回失效购物车
        return checkoutResult;

    }

    private Boolean changeCartEvent(List<Integer> cartIdList) {
        List<ShoppingCart> carts = cartRepository.findByIdIn(cartIdList);
        carts.forEach(c -> {
            c.setCartEvent(CartEventType.CHECKOUT);
        });
        List<ShoppingCart> cartList = cartRepository.save(carts);
        return !CollectionUtils.isEmpty(cartList);
    }

    public List<MemberCouponVo> mergeCoupons(List<MemberCouponDto> couponList) {
        List<MemberCouponVo> couponVoList = new ArrayList<>();
        String shopCouponUrl = "http://chu-marketing/discountCoupons/%s";
        for (MemberCouponDto couponDto : couponList) {
            MemberCouponVo couponVo = new MemberCouponVo();
            BeanUtil.copyProperties(couponVo, couponDto);
            ResponseEntity<DiscountCouponDto> entity = restTemplate.getForEntity(String.format(shopCouponUrl,
                    couponDto.getCouponDiscountId()), DiscountCouponDto.class);
            DiscountCouponDto dto = entity.getBody();
            BeanUtil.copyProperties(couponVo, dto, true);
            couponVoList.add(couponVo);
        }
        return couponVoList;
    }

    /**
     * 检查可用库存
     *
     * @param currentCart   当前购物车
     * @param productStorck 商品库存
     * @return
     */
    private boolean checkAvailableInventory(ShoppingCartResult currentCart, Map<Integer, Integer> productStorck) {
        boolean hasInventory = true;
        try {
            List<ShopCart> shopCart = currentCart.getShopCart();
            List<CartItem> unInventoryCartItems = Lists.newArrayList();
            shopCart.forEach(s -> {
                List<CartItem> tempCartItems = s.getCartItems().stream().
                        filter(e -> productStorck.get(e.getProductId()) - e.getQuantity() < 0).collect(Collectors.toList());
                unInventoryCartItems.addAll(tempCartItems);
            });
            if (unInventoryCartItems.size() > 0) {
                String productIds = unInventoryCartItems.stream().map(c -> c.getProductId() + "")
                        .collect(Collectors.joining(","));
                throw new ChuException(String.format("以下产品:%s.没有足够的库存可用," +
                        "请降低这些产品的数量再次尝试.", productIds));
            }
        } catch (Exception e) {
            if (e instanceof ChuException) {
                throw e;
            }
            log.error("检查是否有可用的库存时出错", e);
            hasInventory = false;
        }
        return hasInventory;
    }

    public ShoppingCartResult fallbackCart(Integer memberId, Throwable e) {
        log.error("获取用户购物车服务熔断,异常信息:", e);
        return new ShoppingCartResult();
    }

    /**
     * 聚合用户购物车,聚合事件为 ADD_ITEM,数据库中的一条记录触发加入购物车事件,移除事件,检出事件,清除事件
     * 只会存在一个事件状态
     * <p>
     *
     * @param shoppingCarts 用户购物车列表
     * @return 个表示用户购物车聚合状态的购物车
     */
    private ShoppingCartResult aggregateUserCarts(List<ShoppingCart> shoppingCarts) {
        ShoppingCartResult cartResult = new ShoppingCartResult();
        Map<Integer, List<ShoppingCart>> shopShoppingCarts = shoppingCarts.stream().collect(groupingBy(ShoppingCart::getShopId));
        String shopUrl = "http://chu-product/shop/%s";
        List<ShopCart> shopCartList = Lists.newArrayList();
        List<CartItem> loseCarts = new ArrayList<>();
        shopShoppingCarts.forEach((shopId, shopCart) -> {
            ResponseEntity<ShopInfo> responseEntity = restTemplate.getForEntity(String.format(shopUrl, shopId), ShopInfo.class);
            ShopInfo shopInfo = responseEntity.getBody();
            List<CartItem> cartItems = shopCart.stream().map(e -> covertCartItem(e, loseCarts)).collect(Collectors.toList());
            cartItems = cartItems.stream().filter(e -> Objects.equals(e.getEffective(), true)).collect(Collectors.toList());
            ShopCart sc = new ShopCart();
            BeanUtil.copyProperties(sc, shopInfo);
            sc.setCartItems(cartItems);
            shopCartList.add(sc);
        });
        cartResult.setShopCart(shopCartList);
        cartResult.setLoseProducts(loseCarts);
        return cartResult;
    }

    private CartItem covertCartItem(ShoppingCart cart, List<CartItem> loseItems) {
        String productUrl = "http://chu-product/products/%s";
        ResponseEntity<ProductVo> entity = restTemplate.getForEntity(String.format(productUrl, cart.getProductId()), ProductVo.class);
        CartItem cartItem;
        if (entity.getBody().getStatus().equals(0)) {
            cartItem = new CartItem(true, cart.getId(), cart.getProductId(), entity.getBody(), cart.getNum(), entity.getBody().getPrice());
        } else {
            cartItem = new CartItem(false, cart.getId(), cart.getProductId(), entity.getBody(), cart.getNum(), entity.getBody().getPrice());
            loseItems.add(cartItem);
        }
        return cartItem;
    }

    /**
     * 将当前购物车所有的商品Id,查询出商品集合
     *
     * @param shopCart 购物车
     * @return 商品与库存的Map, key :商品Id,value:库存
     */
    private Map<Integer, Integer> productStock(List<ShopCart> shopCart) {
        Map<Integer, Integer> productStorck = new HashMap<>();
        List<Integer> productId = new ArrayList<>();
        shopCart.forEach(e -> {
            List<Integer> productIds = e.getCartItems().stream().map(c -> c.getProductId()).collect(Collectors.toList());
            productId.addAll(productIds);
        });
        ResponseMessage<List<ProductVo>> response = productFeignClient.findByIdIn(productId);
        List<ProductVo> productVos = response.getData();
        if (productVos.size() > 0) {
            productStorck = productVos.stream()
                    .collect(Collectors.toMap(ProductVo::getId, ProductVo::getStocks));
        }
        return productStorck;
    }
}